﻿using System;
using System.Text;
using Test30.Core;
using Test30.Util;

namespace Test30.Engine
{
    public class Item : Thing, IComparable<Item>,ICollectible<ItemCollection, Item>
    {
        internal event EventHandler Changed;
        public ItemType Type { get; }
        public Power Power { get; }
        public int Quantity
        {
            get => _quantity;
            set
            {
                if (_quantity != value)
                {
                    _quantity = value;
                    Changed?.Invoke(this, EventArgs.Empty);
                }
            }
        }
        public int Charges
        {
            get => _charges;
            set
            {
                if (_charges != value)
                {
                    _charges = value;

                    Changed?.Invoke(this, EventArgs.Empty);
                }
            }
        }
        public Attack Attack
        {
            get
            {
                // get the base attack from the type
                Attack attack = Type.Attack;

                // apply the brand if there is one
                if (Power?.Type.Element != null)
                {
                    attack = new Attack(attack, Power.Type.Element.Value);
                }

                return attack;
            }
        }
        public int StrikeBonus
        {
            get
            {
                int bonus = 0;

                if (Attack != null) bonus += Attack.StrikeBonus;
                if (Power != null) bonus += Power.StrikeBonus;

                return bonus;
            }
        }
        public float DamageBonus
        {
            get
            {
                float bonus = 1.0f;

                if (Attack != null) bonus *= Attack.DamageBonus;
                if (Power != null) bonus *= Power.DamageBonus;

                return bonus;
            }
        }
        public int ArmorBonus
        {
            get
            {
                if (Power != null) return Power.ArmorBonus;

                return 0;
            }
        }
        public int StatBonus
        {
            get
            {
                if (Power != null) return Power.StatBonus;

                return 0;
            }
        }
        public int SpeedBonus
        {
            get
            {
                if (Power != null) return Power.SpeedBonus;

                return 0;
            }
        }
        public IFlagCollection Flags
        {
            get
            {
                if (Power == null) return Type.Flags;

                return new MergedFlagCollection(Type.Flags, Power.Flags);
            }
        }
        public Item(Vec pos, ItemType type, Power power, int quantity, int charges)
            : base(pos)
        {
            Type = type;
            Power = power;
            _quantity = quantity;
            _charges = charges;
        }
        public Item(Vec pos, ItemType type, int quantity) : this(pos, type, null, quantity, type.Charges.Roll()) { }

        public Item(Vec pos, ItemType type) : this(pos, type, type.Quantity.Roll()) { }

        public Item(Item clone, int quantity, int charges) : this(clone.Position, clone.Type, clone.Power, quantity, charges) { }

        public override int LightRadius
        {
            get
            {
                // no light radius for unlit lights
                if ((Type.ChargeType == ChargeType.Light) && (Charges <= 0)) return -1;

                return Type.LightRadius;
            }
        }

        public override Dungeon Dungeon => _collection.Dungeon;
        public override string Name => ToString();
        public int GetStatBonus(Stat stat)
        {
            if (Flags.Has("raise:" + stat.Name.ToLower()))
            {
                return StatBonus;
            }

            // bonus does not apply to the given stat
            return 0;
        }
        public override void Hit(Action action, Hit hit)
        {
            // apply elemental effects
            if (((hit.Attack.Element == Element.Water) ||
                 (hit.Attack.Element == Element.Acid)) && Flags.Has("dissolves"))
            {
                //### bob: should get a saving throw
                //### bob: hack. assumes it is on the ground
                _collection.Remove(this);
            }
            else if ((hit.Attack.Element == Element.Fire) && Flags.Has("burns up"))
            {
                //### bob: should get a saving throw
                //### bob: hack. assumes it is on the ground
                _collection.Remove(this);
            }
            else if ((hit.Attack.Element == Element.Fire) && Flags.Has("melts"))
            {
                //### bob: should get a saving throw
                //### bob: hack. assumes it is on the ground
                _collection.Remove(this);
            }
        }
        public static Item Random(Vec pos, ItemType type, int level)
        {
            Item item = new Item(pos, type);

            return item;
        }
        public bool Resists(Element element)
        {
            return Flags.Has("resist:" + element.ToString().ToLower());
        }
        public Action TakeTurn(Game game)
        {
            switch (Type.ChargeType)
            {
                case ChargeType.Light:
                    if (Charges > 0) return new BurnAction(game, this);
                    break;
            }

            return null;
        }

        #region IComparable<Item> Members
        public int CompareTo(Item other)
        {
            // first sort by type
            int compare = Type.CompareTo(other.Type);
            if (compare != 0) return compare;

            // if those are the same, sort by quantity (greatest first)
            return -Quantity.CompareTo(other.Quantity);
        }
        #endregion

        #region ICollectible<ItemCollection,Item> Members
        public void SetCollection(ItemCollection collection)
        {
            _collection = collection;
        }
        #endregion
        #region INoun Members

        public override string NounText => Name;
        public override Person Person => Person.Third;
        public override string Pronoun => "it";
        public override string Possessive => "its";

        #endregion

        public override string ToString()
        {
            return ToString(ItemStringOptions.Default);
        }
        public string ToString(ItemStringOptions options)
        {
            return ToString(Quantity, options);
        }
        public string ToString(int quantity, ItemStringOptions options)
        {
            StringBuilder builder = new StringBuilder();

            // show the quantity
            if (options.IsSet(ItemStringOptions.ShowQuantity))
            {
                if (quantity > 1)
                {
                    builder.Append(quantity.ToString());
                    builder.Append(" ");
                }
                else
                {
                    builder.Append("a ");
                }
            }

            // show the prefix power
            if ((Power != null) && Power.Type.IsPrefix)
            {
                builder.Append(Power.Name);
                builder.Append(" ");
            }

            // show the name
            if (options.IsSet(ItemStringOptions.ShowQuantity))
            {
                if (quantity > 1)
                {
                    builder.Append(Type.Noun);
                }
                else
                {
                    builder.Append(Type.Noun);
                }
            }
            else
            {
                builder.Append(Type.Noun);
            }

            // show the suffix power
            if ((Power != null) && !Power.Type.IsPrefix)
            {
                builder.Append(" ");
                builder.Append(Power.Name);
            }

            if (options.IsSet(ItemStringOptions.ShowBonuses))
            {
                // show the weapon stats
                if ((Type.Attack != null) || (StrikeBonus != 0) || (Math.Abs(DamageBonus - 1.0f) > 1e-8))
                {
                    bool needsSpace = false;
                    builder.Append(" ^k(^m");

                    if (Type.Attack != null)
                    {
                        builder.Append(Type.Attack.Damage);
                        needsSpace = true;
                    }

                    if (StrikeBonus != 0)
                    {
                        if (needsSpace) builder.Append(" ");

                        builder.Append(StrikeBonus.ToString("+##;-##;0"));
                        needsSpace = true;
                    }

                    if (Math.Abs(DamageBonus - 1.0f) > 1e-8)
                    {
                        if (needsSpace) builder.Append(" ");

                        builder.AppendFormat("x{0}", DamageBonus);
                    }

                    builder.Append("^k)^-");
                }

                // show the armor stats
                if ((Type.Armor != 0) || (ArmorBonus != 0))
                {
                    bool needsSpace = false;
                    builder.Append(" ^k[^m");

                    if (Type.Armor != 0)
                    {
                        builder.Append(Type.Armor.ToString());
                        needsSpace = true;
                    }

                    if (ArmorBonus != 0)
                    {
                        if (needsSpace) builder.Append(" ");

                        builder.Append(ArmorBonus.ToString("+##;-##;0"));
                    }

                    builder.Append("^k]^-");
                }

                // show the stat bonus
                if (StatBonus != 0)
                {
                    builder.Append(" ^k(^m");
                    builder.Append(StatBonus.ToString("+##;-##;0"));
                    builder.Append("^k)^-");
                }

                // show the speed bonus
                if (SpeedBonus != 0)
                {
                    builder.Append(" ^k(^m");
                    builder.Append(SpeedBonus.ToString("+##;-##;0"));
                    builder.Append(" speed^k)^-");
                }
            }

            if (options.IsSet(ItemStringOptions.ShowCharges))
            {
                switch (Type.ChargeType)
                {
                    case ChargeType.Light:
                        if (Charges == 0)
                        {
                            builder.Append(" ^k(^mempty^k)^-");
                        }
                        else if (Charges > 0)
                        {
                            builder.AppendFormat(" ^k(^ylit^m {0} left^k)^-", Charges);
                        }
                        else
                        {
                            builder.AppendFormat(" ^k(^m{0} left^k)^-", -Charges);
                        }
                        break;

                    case ChargeType.Multi:
                        if (Charges == 0)
                        {
                            builder.Append(" ^k(^mempty^k)^-");
                        }
                        else
                        {
                            builder.AppendFormat(" ^k(^m{0} charges^k)^-", Charges);
                        }
                        break;
                }
            }

            return builder.ToString();
        }

        private ItemCollection _collection;
        private int _quantity;
        private int _charges;
    }

    public static class ItemStringOptionsExtensions
    {
        public static bool IsSet(this ItemStringOptions options, ItemStringOptions flags)
        {
            return (options & flags) == flags;
        }
    }

}
